package ru.amse.tsyganov.jumleditor.storing;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import org.w3c.dom.Document;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import ru.amse.tsyganov.jumleditor.model.ActivityGraph;
import ru.amse.tsyganov.jumleditor.view.representation.ActivityGraphView;

public class Storage {
	
	private static class MyErrorHandler implements ErrorHandler {
		
		private final StringBuilder errors = new StringBuilder();
		private boolean withErrors = false;

		public void error(SAXParseException exception) throws SAXException {
			registerError(exception);
		}

		private void registerError(Exception exception) {
			withErrors = true;
			errors.append(exception.getMessage()).append('\n');
		}

		public void fatalError(SAXParseException exception) throws SAXException {
			throw exception;
		}

		public void warning(SAXParseException exception) throws SAXException {
			registerError(exception);
		}

		public StringBuilder getErrors() {
			return errors;
		}

		public boolean isWithErrors() {
			return withErrors;
		}
		
	}
	
	public static void write(
			ActivityGraphView<? extends ActivityGraph> activityGraph,
			String path) throws IOException, StoreException {
		
		if (activityGraph == null || path == null) {
			throw new IllegalArgumentException();
		}
		
		File outputFile = new File(path);
		
		if (outputFile.exists()) {
			outputFile.delete();
		}
		outputFile.createNewFile();
		
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = null;
		Document root;
		try {
			builder = factory.newDocumentBuilder();	
		} catch (ParserConfigurationException e1) {
			throw new StoreException(e1);
		}
		root = builder.newDocument();
				
		XmlWriteVisitor xmlWriteVisitor = new XmlWriteVisitor(root, false);
		activityGraph.accept(xmlWriteVisitor);
		xmlWriteVisitor.setVisitTransitions(true);
		activityGraph.accept(xmlWriteVisitor);
		
		writeToFile(outputFile, root);
	}

	private static void writeToFile(File outputFile, Document root) throws FileNotFoundException, TransformerFactoryConfigurationError, StoreException, IOException {
		DOMSource source = new DOMSource(root);
		
		FileOutputStream os = new FileOutputStream(outputFile);
		StreamResult result = new StreamResult(os);
		
		TransformerFactory transFactory = TransformerFactory.newInstance();
		try {
			Transformer transformer = transFactory.newTransformer();
			transformer.transform(source, result);
		} catch (TransformerConfigurationException e) {
			throw new StoreException(e);
		} catch (TransformerException e) {
			throw new StoreException(e);
		} finally {
			os.close();
		}
	}
	
	public static void read(String path, ActivityGraphView<ActivityGraph> activityGraphView) 
		throws IOException, StoreException {
		
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder;
		Document document;
		
		SchemaFactory schemaFactory = 
			SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
				
		try {
			Schema schema = schemaFactory.newSchema(new File("ValidationSchema.xsd"));
			factory.setSchema(schema);
			factory.setValidating(false);
			factory.setNamespaceAware(true);
			builder = factory.newDocumentBuilder();
			MyErrorHandler errorHandler = new MyErrorHandler();
			builder.setErrorHandler(errorHandler);
			document = builder.parse(new File(path));
			
			if (errorHandler.isWithErrors()) {
				throw new StoreException(errorHandler.getErrors().toString());
			}
		} catch (ParserConfigurationException e) {
			throw new StoreException(e);
		} catch (SAXException e) {
			e.printStackTrace();
			throw new StoreException(e);
		}	
		
		XmlReader xmlReader = new XmlReader();
		
		xmlReader.readFromDocument(document, activityGraphView);
	}
}
